package com.ruiysoft.security.exception;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.common.DefaultThrowableAnalyzer;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
import org.springframework.security.web.util.ThrowableAnalyzer;
import org.springframework.stereotype.Component;
import org.springframework.web.HttpRequestMethodNotSupportedException;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * Created by 化增光 on 2018/9/21.
 */
@Component("customWebResponseExceptionTranslator")
public class CustomWebResponseExceptionTranslator implements WebResponseExceptionTranslator {

    private ThrowableAnalyzer throwableAnalyzer = new DefaultThrowableAnalyzer();

    @Override
    public ResponseEntity<OAuth2Exception> translate(Exception e) throws Exception {
        e.printStackTrace();
        Throwable[] causeChain = this.throwableAnalyzer.determineCauseChain(e);
        OAuth2Exception ase = (OAuth2Exception)this.throwableAnalyzer.getFirstThrowableOfType(OAuth2Exception.class, causeChain);
        if(ase != null) {
            return this.handleOAuth2Exception(ase);
        }else{
            AuthenticationException ase1 = (AuthenticationException)this.throwableAnalyzer.getFirstThrowableOfType(AuthenticationException.class, causeChain);
            if(ase1 != null) {
                return this.handleOAuth2Exception(new UnauthorizedException(e.getMessage(), e));
            } else {
                AccessDeniedException ase2 = (AccessDeniedException)this.throwableAnalyzer.getFirstThrowableOfType(AccessDeniedException.class, causeChain);
                if(ase2 instanceof AccessDeniedException) {
                    return this.handleOAuth2Exception(new ForbiddenException(ase2.getMessage(), ase2));
                } else {
                    if (e instanceof IllegalArgumentException){
                       return this.handleOAuth2Exception(new ServerErrorException(e.getMessage(), e));
                    }
                    HttpRequestMethodNotSupportedException ase3 = (HttpRequestMethodNotSupportedException)this.throwableAnalyzer.getFirstThrowableOfType(HttpRequestMethodNotSupportedException.class, causeChain);
                    return ase3 instanceof HttpRequestMethodNotSupportedException?this.handleOAuth2Exception(new MethodNotAllowed(ase3.getMessage(), ase3)):this.handleOAuth2Exception(new ServerErrorException(HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase(), e));
                }
            }
        }

    }

    private static class MethodNotAllowed extends OAuth2Exception {
        public MethodNotAllowed(String msg, Throwable t) {
            super(msg, t);
        }

        public String getOAuth2ErrorCode() {
            return "method_not_allowed";
        }

        public int getHttpErrorCode() {
            return 405;
        }
    }

    private ResponseEntity<OAuth2Exception> handleOAuth2Exception(OAuth2Exception e) throws IOException {
        return ResponseEntity
                .status(e.getHttpErrorCode())
                .body(new CustomOauthException(e.getMessage()));
//        HttpHeaders headers = new HttpHeaders();
//        headers.set("Cache-Control", "no-store");
//        headers.set("Pragma", "no-cache");
//        if(status == HttpStatus.UNAUTHORIZED.value() || e instanceof InsufficientScopeException) {
//            headers.set("WWW-Authenticate", String.format("%s %s", new Object[]{"Bearer", e.getSummary()}));
//        }

//        ResponseEntity response = new ResponseEntity(e, headers, HttpStatus.valueOf(status));
//        return response;
    }

    private static class UnauthorizedException extends OAuth2Exception {
        public UnauthorizedException(String msg, Throwable t) {
            super(msg, t);
        }

        public String getOAuth2ErrorCode() {
            return "unauthorized";
        }

        public int getHttpErrorCode() {
            return HttpServletResponse.SC_UNAUTHORIZED;
        }
    }

    private static class ServerErrorException extends OAuth2Exception {
        public ServerErrorException(String msg, Throwable t) {
            super(msg, t);
        }

        public String getOAuth2ErrorCode() {
            return "server_error";
        }

        public int getHttpErrorCode() {
            return HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
        }
    }

    private static class ForbiddenException extends OAuth2Exception {
        public ForbiddenException(String msg, Throwable t) {
            super(msg, t);
        }

        public String getOAuth2ErrorCode() {
            return "access_denied";
        }

        public int getHttpErrorCode() {
            return HttpServletResponse.SC_FORBIDDEN;
        }
    }

}
